home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """
- Integration of debconf on the client side
-
- Provides the DebconfProxy class which allows to run the debconf frontend
- as normal user by connecting to the root running debconf through the
- socket of the passthrough frontend.
- """
- # Copyright (C) 2009 Sebastian Heinlein <devel@glatzor.de>
- # Copyright (C) 2009 Michael Vogt <michael.vogt@ubuntu.com>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- import copy
- import glib
- import logging
- import os
- import os.path
- import shutil
- import socket
- import subprocess
- import tempfile
-
- import gobject
-
- log = logging.getLogger("AptClient.DebconfProxy")
-
- class DebconfProxy(object):
-
- """The DebconfProxy class allows to run the debconf frontend
- as normal user by connecting to the root debconf through the socket of the
- passthrough frontend.
- """
-
- def __init__(self, frontend="gnome", socket_path=None):
- """Initialize a new DebconfProxy instance.
-
- Keyword arguments:
- frontend -- the to be used debconf frontend (defaults to gnome)
- socket_path -- the path to the socket of the passthrough frontend.
- Will be created if not specified
- """
- self.socket_path = socket_path
- self.temp_dir = None
- if socket_path is None:
- self.temp_dir = tempfile.mkdtemp(prefix="aptdaemon-")
- self.socket_path = os.path.join(self.temp_dir, "debconf.socket")
- log.debug("debconf socket: %s" % self.socket_path)
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- self.socket.bind(self.socket_path)
- self.frontend = frontend
- self._listener_id = None
- self._active_conn = None
- self._watch_ids = []
-
- def _get_debconf_env(self):
- """Returns a dictonary of the environment variables required by
- the debconf frontend.
- """
- env = copy.copy(os.environ)
- env["DEBCONF_DB_REPLACE"] = "configdb"
- env["DEBCONF_DB_OVERRIDE"] = "Pipe{infd:none outfd:none}"
- env["DEBIAN_FRONTEND"] = self.frontend
- if log.level == logging.DEBUG:
- env["DEBCONF_DEBUG"] = "."
- return env
-
- def start(self):
- """Start listening on the socket."""
- logging.debug("debconf.start()")
- self.socket.listen(1)
- self._listener_id = gobject.io_add_watch(self.socket, gobject.IO_IN,
- self._accept_connection)
-
- def stop(self):
- """Stop listening on the socket."""
- logging.debug("debconf.stop()")
- self.socket.close()
- # ensure outstanding gio messages are processed
- context = glib.main_context_default()
- while context.pending():
- context.iteration()
- gobject.source_remove(self._listener_id)
- self._listener_id = None
-
- def _accept_connection(self, source, condition):
- """Callback for new connections of the passthrough frontend."""
- log.debug("New passthrough connection")
- # ensure outstanding gio messages are processed (to ensure
- # that _hangup was run on the previous connection, see LP: #432607)
- context = glib.main_context_default()
- while context.pending():
- context.iteration()
- if self._active_conn:
- raise IOError, "Multiple debconf connections not supported"
- conn, addr = source.accept()
- self._active_conn = conn
- self.helper = subprocess.Popen(["debconf-communicate"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- env=self._get_debconf_env())
- w = gobject.io_add_watch(conn, gobject.IO_IN|gobject.IO_HUP|gobject.IO_ERR,
- self._copy_conn, self.helper.stdin)
- self._watch_ids.append(w)
- w= gobject.io_add_watch(self.helper.stdout, gobject.IO_IN,
- self._copy_stdout, conn)
- self._watch_ids.append(w)
- w = gobject.io_add_watch(self.helper.stdout, gobject.IO_HUP|gobject.IO_ERR,
- self._hangup)
- self._watch_ids.append(w)
- return True
-
- def _hangup(self, source, condition):
- """Callback when the debconf-communicate program exists
- This happend when e.g. the user cancels the frontend
- """
- logging.debug("hangup on %s" % source)
- if self._watch_ids:
- for w in self._watch_ids:
- gobject.source_remove(w)
- self._watch_ids = []
- if self._active_conn:
- self._active_conn.close()
- self._active_conn = None
- if self.helper:
- self.helper.stdin.close()
- self.helper.stdout.close()
- self.helper.wait()
- self.helper = None
- return False
-
- def _copy_stdout(self, source, condition, conn):
- """Callback to copy data from the stdout of debconf-communicate to
- the passthrough frontend."""
- logging.debug("_copy_stdout")
- try:
- debconf_data = source.readline()
- if debconf_data:
- log.debug("From debconf: %s", debconf_data)
- conn.send(debconf_data)
- return True
- except:
- pass
- # error, stop listening
- return self._hangup(source, condition)
-
- def _copy_conn(self, source, condition, stdin):
- """Callback to copy data from the passthrough frontend to stdin of
- debconf-communicate."""
- logging.debug("_copy_conn")
- try:
- socket_data = source.recv(1024)
- if socket_data:
- log.debug("From socket: %s", socket_data)
- stdin.write(socket_data)
- return True
- except:
- pass
- # errror, stop listening
- return self._hangup(source, condition)
-
- def _test():
- """Run the DebconfProxy from the command line for testing purposes.
-
- You have to execute the following commands before in a separate terminal:
- $ echo "fset debconf/frontend seen false" | debconf-communicate
- $ export DEBCONF_PIPE=/tmp/debconf.socket
- $ dpkg-reconfigure debconf -f passthrough
- """
- logging.basicConfig(level=logging.DEBUG)
- socket_path="/tmp/debconf.socket"
- if os.path.exists(socket_path):
- os.remove(socket_path)
- proxy = DebconfProxy("gnome", socket_path)
- proxy.start()
- loop = gobject.MainLoop()
- loop.run()
-
- if __name__ == "__main__":
- _test()
-
- #vim:ts=4:sw=4:et
-